# -*- python -*-
# ex: set syntax=python:

# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
import random
import shutil
import subprocess
import sys
import tempfile

# These modules come from scripts/master, which must be in the PYTHONPATH.
from common import cros_chromite
from master import gitiles_poller
from master import master_utils
from master import slaves_list
from master.cros_try_job_git import CrOSTryJobGit, CbuildbotConfigs
from master.factory import annotator_factory, chromeos_factory

from buildbot.buildslave import BuildSlave
from buildbot.process.properties import WithProperties

# These modules come from scripts/common, which must be in the PYTHONPATH.
import chromiumos_tryserver_util
import config
import master_site_config

ActiveMaster = master_site_config.ChromiumOSTryServer

# This is the dictionary that the buildmaster pays attention to. We also use
# a shorter alias to save typing.
c = BuildmasterConfig = {}

config.DatabaseSetup(c)

# Load our Cbuildbot configs.
cbuildbot_configs = CbuildbotConfigs(
    chromiumos_tryserver_util.configs,
    etc_builder='etc',
)

####### BUILDBUCKET

# Install BuildBucket hooks so we can add/enforce CrOS properties.
cbuildbot_configs.AddBuildBucketHooks(c)

####### CHANGESOURCES

c['change_source'] = []
c['change_source'].append(gitiles_poller.GitilesPoller(
    ActiveMaster.repo_url_ext,
    branches=['master'],
    ))
gitiles_comparator = c['change_source'][-1].comparator

if ActiveMaster.repo_url_int:
  c['change_source'].append(gitiles_poller.GitilesPoller(
      ActiveMaster.repo_url_int,
      branches=['master'],
      comparator=gitiles_comparator,
      ))

# Avoid merging requests.
c['mergeRequests'] = lambda *_: False


####### JOB AND BUILDER SELECTION ALGORITHM

DEFINED_SLAVES = slaves_list.SlavesList('slaves.cfg', 'ChromiumOSTryServer')

# We isolate testing slaves here - we can force their use through the try job.
testing_slave_pool = chromiumos_tryserver_util.TestingSlavePool(
    [slave['hostname']
     for slave in DEFINED_SLAVES.GetSlaves()
     if slave['version'] == 'testing'])

####### BUILDERS

c['builders'] = []

# ----------------------------------------------------------------------------
# BUILDER DEFINITIONS

nextSlaveAndBuild = chromiumos_tryserver_util.NextSlaveAndBuild(
    testing_slave_pool=testing_slave_pool)

# Annotator factory object.
factory_obj = annotator_factory.AnnotatorFactory(
    active_master=ActiveMaster)

# The 'builders' list defines the Builders. Each one is configured with a
# dictionary, using the following keys:
#  name (required): the name used to describe this bilder
#  category (required): it is not used in the normal 'buildbot' meaning. It is
#                       used by gatekeeper to determine which steps it should
#                       look for to close the tree.
def _GetCBuildbotBuilder(name, category):
  builder = {
      'name': name,
      'builddir': name.replace(' ', '-'),
      'category': category,
      'factory': chromeos_factory.ChromiteRecipeFactory(
          factory_obj, 'cros/cbuildbot_tryjob'),
      'slavenames': DEFINED_SLAVES.GetSlavesName(builder=name),
      'nextSlaveAndBuild' : nextSlaveAndBuild,
  }
  return builder

def _GetCategory(cfg):
  if cfg in ('etc',):
    return '1etc full|info'
  elif cfg in chromiumos_tryserver_util.precq_builders:
    return '2precq full|info'
  elif cfg in chromiumos_tryserver_util.cbb_builders:
    return '3general full|info'
  else:
    raise TypeError("Unhandled builder type: %s" % (type(cfg).__name__))


def _GetBuilder(cfg):
  builder = _GetCBuildbotBuilder(
      cfg,
      _GetCategory(cfg),
  )
  return builder


# Add 'etc' builder to try arbitrary configs.
c['builders'] += [_GetBuilder(cbuildbot_configs.etc_builder)]

# Add Try builders for every current 'cbuildbot' config.
c['builders'] += [_GetBuilder(cfg)
                  for cfg in sorted(chromiumos_tryserver_util.cbb_builders)]

# Sort builders by category, then name.
c['builders'].sort(key=lambda b: b['category'])

####### BUILDSLAVES

# the 'slaves' list defines the set of allowable buildslaves. Each element is a
# tuple of bot-name and bot-password. These correspond to values given to the
# buildslave's mktap invocation.
c['slaves'] = master_utils.AutoSetupSlaves(c['builders'],
                                           config.Master.GetBotPassword())
assert c['slaves'], "Failed to apply slave configuration!"

####### SCHEDULERS

smtp_host = config.Master.smtp if ActiveMaster.is_production_host else 'smtp'
email_footer = """
<strong>Please send bugs and questions to %(reply_to)s.  You can
also reply to this email.</strong>
""" % {'reply_to' : ActiveMaster.reply_to}
c['schedulers'] = []
c['schedulers'].append(CrOSTryJobGit(
    name='cros_try_job_git',
    pollers=c['change_source'][:],
    smtp_host=smtp_host,
    from_addr=ActiveMaster.from_address,
    reply_to=ActiveMaster.reply_to,
    email_footer=email_footer,
    cbuildbot_configs=cbuildbot_configs,
))


####### STATUS TARGETS

# Buildbot master url:
# Must come before AutoSetupMaster().
c['buildbotURL'] = ActiveMaster.buildbot_url

# Adds common status and tools to this master.
web_template_globals = {
    'cros_slave_name': testing_slave_pool.cros_slave_name,
    'cros_builder_links': chromiumos_tryserver_util.cros_builder_links,
}
master_utils.AutoSetupMaster(c, ActiveMaster, order_console_by_time=True,
                             public_html='../master.chromiumos/public_html',
                             templates=[ '../master.chromiumos/templates',
                                         '../master.chromium/templates'],
                             web_template_globals=web_template_globals,
                             )

# Add a dumb MailNotifier first so it will be used for BuildSlave with
# notify_on_missing set when they go missing.
from buildbot.status import mail
c['status'].append(mail.MailNotifier(
    fromaddr=ActiveMaster.from_address,
    builders=[],
    relayhost=config.Master.smtp,
    lookup=master_utils.UsersAreEmails()))

# Try job result emails.
from master.try_mail_notifier import TryMailNotifier

def _GetInfo(info, build):
  """Get the subject of a trybot email."""
  info = info.copy()
  info['cbb_config'] = build.getProperties().getProperty('cbb_config',
                                                         info['builder'])
  return info

c['status'].append(TryMailNotifier(
    reply_to=ActiveMaster.reply_to,
    failure_message='TRY FAILED',
    footer=email_footer,
    fromaddr=ActiveMaster.from_address,
    subject="try %(result)s for %(reason)s on %(cbb_config)s",
    mode='all',
    get_info=_GetInfo,
    relayhost=smtp_host,
    lookup=master_utils.UsersAreEmails()))

# Do it at the end to override values set by AutoSetupMaster, the default is
# too low. Must keep at least a few days worth of builds.
c['buildHorizon'] = 3000
c['logHorizon'] = 3000
# Must be at least 2x the number of slaves.
c['eventHorizon'] = 200
